#include <unistd.h>
#include "service.h"
#include "z_memory.h"
#include "z_logger.h"
#include "aliot.h"
#include "core_mqtt.h"
#include "aiot_ota_api.h"
#include "ota_private.h"

static void _download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata) {
    uint32_t data_buffer_len = 0;
    uint32_t last_percent = 0;
    int32_t percent = 0;

    /* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */
    if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) {
        return;
    }
    percent = packet->data.percent;

    /* userdata可以存放 demo_download_recv_handler() 的不同次进入之间, 需要共享的数据 */
    /* 这里用来存放上一次进入本回调函数时, 下载的固件进度百分比 */
    if (userdata) {
        last_percent = *((uint32_t *) (userdata));
    }

    data_buffer_len = packet->data.len;

    /* 如果 percent 为负数, 说明发生了收包异常或者digest校验错误 */
    if (percent < 0) {
        printf("exception: percent = %d\r\n", percent);
        if (userdata) {
            free(userdata);
        }
        return;
    }

    /*
     * TODO: 下载一段固件成功, 这个时候, 用户应该将
     *       起始地址为 packet->data.buffer, 长度为 packet->data.len 的内存, 保存到flash上
     *
     *       如果烧写flash失败, 还应该调用 aiot_download_report_progress(handle, -4) 将失败上报给云平台
     *       备注:协议中, 与云平台商定的错误码在 aiot_ota_protocol_errcode_t 类型中, 例如
     *           -1: 表示升级失败
     *           -2: 表示下载失败
     *           -3: 表示校验失败
     *           -4: 表示烧写失败
     *
     *       详情可见 https://help.aliyun.com/document_detail/85700.html
     */

    /* percent 入参的值为 100 时, 说明SDK已经下载固件内容全部完成 */
    if (percent == 100) {
        /*
         * TODO: 这个时候, 一般用户就应该完成所有的固件烧录, 保存当前工作, 重启设备, 切换到新的固件上启动了
                 并且, 新的固件必须要以

                 aiot_ota_report_version(ota_handle, new_version);

                 这样的操作, 将升级后的新版本号(比如1.0.0升到1.1.0, 则new_version的值是"1.1.0")上报给云平台
                 云平台收到了新的版本号上报后, 才会判定升级成功, 否则会认为本次升级失败了
                 如果下载成功后升级失败, 还应该调用 aiot_download_report_progress(handle, -1) 将失败上报给云平台
         */
    }

    /* 简化输出, 只有距离上次的下载进度增加5%以上时, 才会打印进度, 并向服务器上报进度 */
    if (percent - last_percent >= 5 || percent == 100) {
        log_debug("download %03d%% done, +%d bytes", percent, data_buffer_len);
        aiot_download_report_progress(handle, percent);

        if (userdata) {
            *((uint32_t *) (userdata)) = percent;
        }
        if (percent == 100 && userdata) {
            free(userdata);
        }
    }
}

static void _ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata) {
//    switch (ota_msg->type) {
//        case AIOT_OTARECV_FOTA: {
//            uint32_t res = 0;
//            uint16_t port = 443;
//            uint32_t max_buffer_len = (8 * 1024);
//            aiot_sysdep_network_cred_t cred;
//            void *dl_handle = NULL;
//            void *last_percent = NULL;
//
//            if (NULL == ota_msg->task_desc) {
//                break;
//            }
//
//            dl_handle = aiot_download_init();
//            if (NULL == dl_handle) {
//                break;
//            }
//            printf("OTA target firmware version: %s, size: %u Bytes \r\n", ota_msg->task_desc->version,
//                   ota_msg->task_desc->size_total);
//
//            if (NULL != ota_msg->task_desc->extra_data) {
//                printf("extra data: %s\r\n", ota_msg->task_desc->extra_data);
//            }
//
//            memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
//            cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA;
//            cred.max_tls_fragment = 16384;
//            cred.x509_server_cert = ali_ca_cert;
//            cred.x509_server_cert_len = strlen(ali_ca_cert);
//
//            /* 设置下载时为TLS下载 */
//            aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_CRED, (void *) (&cred));
//            /* 设置下载时访问的服务器端口号 */
//            aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_PORT, (void *) (&port));
//            /* 设置下载的任务信息, 通过输入参数 ota_msg 中的 task_desc 成员得到, 内含下载地址, 固件大小, 固件签名等 */
//            aiot_download_setopt(dl_handle, AIOT_DLOPT_TASK_DESC, (void *) (ota_msg->task_desc));
//            /* 设置下载内容到达时, SDK将调用的回调函数 */
//            aiot_download_setopt(dl_handle, AIOT_DLOPT_RECV_HANDLER, (void *) (demo_download_recv_handler));
//            /* 设置单次下载最大的buffer长度, 每当这个长度的内存读满了后会通知用户 */
//            aiot_download_setopt(dl_handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, (void *) (&max_buffer_len));
//            /* 设置 AIOT_DLOPT_RECV_HANDLER 的不同次调用之间共享的数据, 比如例程把进度存在这里 */
//            last_percent = malloc(sizeof(uint32_t));
//            if (NULL == last_percent) {
//                aiot_download_deinit(&dl_handle);
//                break;
//            }
//            memset(last_percent, 0, sizeof(uint32_t));
//            aiot_download_setopt(dl_handle, AIOT_DLOPT_USERDATA, (void *) last_percent);
//
//            /* 启动专用的下载线程, 去完成固件内容的下载 */
//            res = pthread_create(&g_download_thread, NULL, demo_ota_download_thread, dl_handle);
//            if (res != 0) {
//                printf("pthread_create demo_ota_download_thread failed: %d\r\n", res);
//                aiot_download_deinit(&dl_handle);
//                free(last_percent);
//            } else {
//                /* 下载线程被设置为 detach 类型, 固件内容获取完毕后可自主退出 */
//                pthread_detach(g_download_thread);
//            }
//            break;
//        }
//
//        default:
//            break;
//    }
}

void *_download_service_init(aliot_service_args_t *args) {
    aiot_download_task_desc_t *task_desc = args->userdata;
    void *handle = aiot_download_init();

    uint32_t start = 0, end = task_desc->size_total;
    uint32_t buf_size = OTA_DEFAULT_DOWNLOAD_BUFLEN;
    uint32_t timeout = OTA_DEFAULT_DOWNLOAD_TIMEOUT_MS;

    aiot_download_setopt(handle, AIOT_DLOPT_RECV_TIMEOUT_MS, &timeout);
    aiot_download_setopt(handle, AIOT_DLOPT_RANGE_START, &start);
    aiot_download_setopt(handle, AIOT_DLOPT_RANGE_END, &end);
    aiot_download_setopt(handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, &buf_size);
    aiot_download_setopt(handle, AIOT_DLOPT_RECV_HANDLER, _download_recv_handler);
    aiot_download_setopt(handle, AIOT_DLOPT_TASK_DESC, task_desc);

    return handle;
}

static void _download_service_fini(void **handle) {
    aiot_download_deinit(handle);
}

static void *_download_service_start(void *handle) {
    aiot_download_send_request(handle);

    while (running) {
        int32_t ret = aiot_download_recv(handle);

        if (STATE_DOWNLOAD_FINISHED == ret) {
            log_info("download completed");
            break;
        } else if (STATE_DOWNLOAD_RENEWAL_REQUEST_SENT == ret) {
            log_debug("download renewal request has been sent successfully");
            continue;
        } else if (ret <= STATE_SUCCESS) {
            log_warn("download failed, error code is %d, try to send renewal request", ret);
            continue;
        } else {
        }
    }
}

static int32_t _download_service_stop(void *handle) {
    return STATE_SUCCESS;
}

static aliot_service_t _download_service = {
        .name = DOWNLOAD_MODULE_NAME,
        .init = _download_service_init,
        .fini = _download_service_fini,
        .start = _download_service_start,
        .stop = _download_service_stop,
};

aliot_service_t *download_service_get() {
    return &_download_service;
}